" NAME latex.st AUTHOR mcconnel@cs.uiuc.edu FUNCTION print out ST source as LaTeX source ST-VERSIONS 2.5, 4.0 PREREQUISITES CONFLICTS DISTRIBUTION world VERSION 1.0 DATE June 21, 1991 SUMMARY latex.st This code causes the print-out menu item in the various browser panes to produce LaTeX. Printing out categories and classes yields self-contained LaTeX documents; printing out protocols and methods yields LaTeX output suitable for inclusion in a LaTeX document. In particular, printing a category yields a document with a table of contents (showing what page the class definitions are on), and so you will need to LaTeX the resulting file twice. Carl McConnell !Browser methodsFor: 'private-category functions'! printOutCategory organization printOutCategory: category! ! !Browser methodsFor: 'private-class functions'! printOutClass self selectedClass printOut! ! !Browser methodsFor: 'private-protocol functions'! printOutProtocol self selectedClass printOutCategory: protocol! ! !Browser methodsFor: 'private-selector functions'! printOutMessage self selectedClass printOutMessage: selector! ! !SystemOrganizer methodsFor: 'fileIn/Out'! printOutCategory: category LatexStream for: category do: [:fileStream | Transcript cr; show: 'Printing out ' , category , ':'; cr. fileStream beginCategory: category. self printOutCategory: category on: fileStream. fileStream endCategory]! printOutCategory: category on: aFileStream | firstTrip | firstTrip _ true. (self superclassOrder: category) do: [:class | "The test prevents a blank page from being printed right after the table of contents." firstTrip ifTrue: [firstTrip _ false] ifFalse: [aFileStream anotherClass]. Transcript tab; show: class name; cr. class printOutOn: aFileStream]! ! !Filename methodsFor: 'stream creation'! latexStream "Answer a write stream connected to the file represented by the receiver." ^LatexStream on: (FileConnection openFileNamed: self mode: #writeOnly creationRule: #truncateOrCreate)! ! ExternalWriteStream subclass: #LatexStream instanceVariableNames: 'inComment lastCharacter generateTableOfContents ' classVariableNames: 'CharacterMap ' poolDictionaries: '' category: 'OS-Streaming'! LatexStream comment: 'I translate Smalltalk into LaTeX. Problems: tables in comments aren''t always correctly aligned. Author: Carl McConnell mcconnel@cs.uiuc.edu Department of Computer Science University of Illinois at Urbana-Champaign 5/90'! !LatexStream methodsFor: 'beginning-ending'! beginCategory: category self generateTableOfContents: true. self beginDocument. self line: '\title{{\bf ', category, '}}'. self line: '\author{', self version, '}'. self line: '\date{' , Date today printString , ' ' , Time now printString , '}'. self line: '\maketitle'. self line: '\pagenumbering{roman}'. self line: '\setcounter{page}{1}'. self line: '\tableofcontents'. self line: '\clearpage'. self line: '\pagenumbering{arabic}'. self line: '\setcounter{page}{1}'. self line: '%'! beginClass self beginDocument. self line: '\begin{center}'. self line: '{\tiny ', self version, '}'. self line: '\end{center}'. self blankLines: 3! endCategory self endDocument! endClass self endDocument! ! !LatexStream methodsFor: 'printing'! printClassComment: aString aString isEmpty ifTrue: [^self]. self blankLines: 1. self beginFont: '\it'. self printSmalltalkString: aString. self endFont; cr! printClassDefinitionFor: aString from: aTable self line: '\begin{center}'. self beginFont: '\Large \bf'. self nextPutAll: aString. self endFont; cr. self line: '\end{center}'. self blankLines: 3. self line: '\markright{{\bf ' , aString , '} \hfill}'. self generateTableOfContents ifTrue: [self line: '\addcontentsline{toc}{section}{' , aString , '}']. self line: '\begin{tabular}{ll}'. aTable do: [:assoc | self nextPutAll: assoc key. self nextPutAll: ' & '. assoc value isEmpty ifTrue: [self beginFont: '\it'; nextPutAll: 'none'; endFont] ifFalse: [self nextPutAll: '\parbox[t]{4.0in}{\raggedright '; beginFont: '\bf'; nextPutAll: assoc value; endFont; nextPut: $}]. assoc = aTable last ifTrue: [self cr] ifFalse: [self line: ' \\']]. self line: '\end{tabular}'! printMethod: aSelector withBody: aString self beginFont: '\bf'; printSmalltalkString: aSelector; endFont. self cr; line: '\nopagebreak'. self printSmalltalkString: aString. self cr! printProtocolHeader: aString self beginFont: '\sl'. self nextPutAll: 'Protocol for '; nextPutAll: aString. self endFont; cr. self line: '\nopagebreak'! ! !LatexStream methodsFor: 'separating'! anotherClass self line: '%'. self line: '\clearpage'! anotherMethod self blankLines: 2! anotherProtocol self blankLines: 3! ! !LatexStream methodsFor: 'private miscellaneous'! blankLines: n self cr. self nextPutAll: '\addvspace{'; print: n; nextPutAll: 'ex}'; cr. self line: '\noindent'! generateTableOfContents ^generateTableOfContents isNil ifTrue: [false] ifFalse: [generateTableOfContents]! generateTableOfContents: aBoolean generateTableOfContents _ aBoolean! line: aString self nextPutAll: aString; cr! version "The version name for Objectworks 4.0 contains a backslash, hence this hack." ^Smalltalk version copyReplaceAll: '\' with: '$\backslash$'! ! !LatexStream methodsFor: 'private printing'! printSmalltalkString: aString | output | inComment _ false. lastCharacter _ nil. aString do: [:aCharacter | output _ CharacterMap at: aCharacter asciiValue. output notNil ifTrue: [self nextPutAll: output] ifFalse: ["This character must require special handling." self printSpecialCharacter: aCharacter]. lastCharacter _ aCharacter]! printSpecialCharacter: aCharacter aCharacter = $" ifTrue: [inComment ifTrue: [self nextPutAll: ''''''; endFont] ifFalse: [self beginFont: '\it'; nextPutAll: '``']. inComment _ inComment not. ^self]. aCharacter = $= ifTrue: [self nextPutAll: (lastCharacter = $: ifTrue: ['='] ifFalse: ['$=$']). ^self]! ! !LatexStream methodsFor: 'private beginning-ending'! beginDocument self line: '\documentstyle[11pt]{report}'. self line: '\oddsidemargin 0in \evensidemargin 0in'. self line: '\marginparwidth 0in \marginparsep 0in'. self line: '\topmargin 0in \topskip 0in'. self line: '\headheight 0.25in \headsep 0.25in'. self line: '\footheight 0in \footskip 0.25in'. self line: '\textheight 8.25in \textwidth 6.5in'. self line: '\raggedbottom \sloppy'. self line: '\pagestyle{myheadings}'. self line: '%'. self line: '\begin{document}'! beginFont: aString self nextPut: ${. self nextPutAll: aString; space! endDocument self line: '\end{document}'! endFont self nextPut: $}! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! LatexStream class instanceVariableNames: ''! !LatexStream class methodsFor: 'class initialization'! at: aCharacter put: anObject CharacterMap at: aCharacter asciiValue put: anObject. ^anObject! createCharacterMap CharacterMap _ Array new: 127. 1 to: 127 do: [:i | CharacterMap at: i put: (String with: (Character value: i))]! initialize "Initialize the table for mapping characters to LaTeX. Characters for which special (or no) action is to be taken have nil entries. We won't have to handle NUL characters, so the table size is 127." | lineEndString first | self createCharacterMap. "These require special handling." self at: $" put: nil. self at: $= put: nil. "We assume that we can distinguish the end-of-line string by its first character; the rest we just ignore." lineEndString _ self lineEndString. first _ true. lineEndString do: [:ch | self at: ch put: (first ifTrue: [first _ false. '\\' , lineEndString] ifFalse: [nil])]. self at: Character tab put: '\hspace*{4ex}'. self at: $# put: '\#'. self at: $$ put: '\$'. self at: $% put: '\%'. self at: $& put: '\&'. self at: $_ put: '$\leftarrow$'. self at: ${ put: '\{'. self at: $} put: '\}'. self at: $~ put: '{\char126}'. self at: $^ put: '$\uparrow$'. self at: $\ put: '$\backslash$'. self at: $| put: '$|$'. self at: $> put: '$>$'. self at: $< put: '$<$'. self at: $+ put: '$+$'. self at: $- put: '$-$'! lineEndString "Return the end-of-line string." | stream | stream _ WriteStream on: (String new: 4). stream cr. ^stream contents! ! !LatexStream class methodsFor: 'instance creation'! for: aString do: aBlock | fileName fileStream | fileName _ Filename request: 'Print LaTeX on' initially: aString , '.tex' shouldExist: nil. fileName = '' ifTrue: [^nil]. fileStream _ (Filename named: fileName) latexStream. [aBlock value: fileStream] valueNowOrOnUnwindDo: [fileStream close]! ! LatexStream initialize! !ClassDescription methodsFor: 'fileIn/Out'! definitionAsTable | table | table _ OrderedCollection new. table add: (Association key: 'class name' value: self name). table add: (Association key: 'superclass' value: (superclass == nil ifTrue: [''] ifFalse: [superclass name])). table add: (Association key: 'instance variable names' value: self instanceVariablesString). table add: (Association key: 'class variable names' value: self classVariablesString). table add: (Association key: 'pool dictionaries' value: self sharedPoolsString). table add: (Association key: 'category' value: self category asString). ^table! printOutCategory: aString LatexStream for: self name , '-' , aString do: [:fileStream | self printOutCategory: aString on: fileStream]! printOutCategory: aString on: aFileStream self printOutCategoryChunk: aString on: aFileStream. (self organization listAtCategoryNamed: aString) do: [:sel | aFileStream anotherMethod. self printOutMethodChunk: sel on: aFileStream]! printOutCategoryChunk: aString on: aFileStream aFileStream printProtocolHeader: aString! printOutMessage: aString LatexStream for: self name , '-' , aString do: [:fileStream | self printOutMethodChunk: aString on: fileStream]! printOutMethodChunk: aSelector on: aFileStream | string parser lastSelectorChar | Cursor write showWhile: [string _ self sourceCodeAt: aSelector. (parser _ self parserClass new) parseSelector: string. lastSelectorChar _ parser endOfLastToken min: string size. ["The parser includes the line separator(s) as part of the selector. Although I don't think it makes any difference in the printed output, I prefer the line separator(s) to be part of the body, since it makes the LaTeX look nicer; hence the loop." (string at: lastSelectorChar) tokenish] whileFalse: [lastSelectorChar _ lastSelectorChar - 1]. aFileStream printMethod: (string copyFrom: 1 to: lastSelectorChar) withBody: (string copyFrom: lastSelectorChar + 1 to: string size)]! printOutOn: aFileStream aFileStream printClassDefinitionFor: self name from: self definitionAsTable. aFileStream printClassComment: self comment. self organization categories do: [:heading | aFileStream anotherProtocol. self printOutCategory: heading on: aFileStream]! ! !Metaclass methodsFor: 'accessing'! category ^thisClass category! ! !Class methodsFor: 'fileIn-Out'! printOut LatexStream for: self name do: [:fileStream | Transcript cr; show: 'Printing out ' , self name. fileStream beginClass. self printOutOn: fileStream. fileStream endClass]! printOutOn: aFileStream super printOutOn: aFileStream. self class nonTrivial ifTrue: [aFileStream anotherClass. self class printOutOn: aFileStream]! !